Skip to content

fix: restore the active top-level view across renderer reload#8265

Merged
nwparker merged 2 commits into
stablyai:mainfrom
myungjuice:fix/persist-active-view-on-reload
Jul 12, 2026
Merged

fix: restore the active top-level view across renderer reload#8265
nwparker merged 2 commits into
stablyai:mainfrom
myungjuice:fix/persist-active-view-on-reload

Conversation

@myungjuice

@myungjuice myungjuice commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Summary / description

The active top-level view (Tasks, Automations, Mobile, Settings, Skills, Space, or the enabled Activity view) now survives a renderer reload or app relaunch instead of always resetting to the terminal.

activeView now uses Orca's existing persisted UI pipeline. Startup hydration restores a validated value once, while later ui:stateChanged broadcasts continue syncing preferences without unexpectedly navigating an already-open window. Unknown values and a disabled experimental Activity view safely fall back to the terminal.

Closes #8264

Evidence of fix

  • Added an Electron E2E regression test that launches Orca twice against the same user-data directory. It opens Tasks on the first launch, verifies activeView reached persisted main-process state, relaunches, and verifies the Tasks DOM is visible while the terminal DOM is hidden.
  • E2E result on current main: 1 passed (8.9s); the restore scenario itself completed in 8.2s.
  • Focused renderer tests: 151 passed across ui.test.ts and startup-ui-hydration.test.ts.
  • All node, CLI, and web TypeScript targets passed.
  • The E2E-mode Electron build, focused Oxlint checks, diff whitespace check, and max-lines ratchet passed.

ELI5

Orca remembered the details inside each screen, but it forgot which screen you were looking at. This change saves that screen name too. When Orca starts again, it checks that the saved name is still valid and opens that screen; background settings updates are not allowed to pull you away from the screen you are currently using.

Trade-offs / expected user regressions

No user regression is expected. The value uses the existing profile-level UI state, so the last desktop view written is the one restored on the next launch; a live window is never navigated by a cross-window/mobile sync. Like the surrounding UI preferences, writes are debounced by 150 ms, so a reload triggered almost immediately after navigation could still restore the prior view.

Screenshots

No visual change. The E2E test provides render-layer evidence for both the restored Tasks page and hidden terminal surface.

Testing

  • pnpm lint (focused Oxlint and the max-lines ratchet passed; the full repository lint suite was not run)
  • pnpm typecheck
  • pnpm test (focused suites passed: 151 tests)
  • pnpm build (the E2E-mode Electron build passed; the full release build was not run)
  • Added or updated high-quality tests that would catch regressions

Commands run:

pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/store/slices/ui.test.ts src/renderer/src/lib/startup-ui-hydration.test.ts
pnpm run typecheck
pnpm check:max-lines-ratchet
pnpm exec oxlint <changed files>
pnpm exec electron-vite build --mode e2e
SKIP_BUILD=1 pnpm exec playwright test tests/e2e/active-view-restart-restore.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1

AI Review Report

Final review checked startup ordering, legacy/corrupt values, experimental-view gating, startup failure fallback, cross-window/mobile rebroadcast behavior, the debounced writer, and the full restart/render path. It identified that the contributor branch was behind main; the branch was merged with current main, then all focused checks were repeated successfully.

Cross-platform review covered macOS, Linux, and Windows. This change adds no shortcuts, shortcut labels, shell commands, platform-specific paths, or OS-specific Electron behavior. It uses the same typed IPC and persistence path on every platform. The SSH/remote use case is preserved because the restored view is app UI state and does not assume a local repository or filesystem path.

Security Audit

The persisted value is treated as untrusted: unknown, missing, legacy, or unavailable view values fall back to terminal. The change adds no command execution, path handling, authentication, secrets, dependencies, or new IPC channel. It only extends the existing trusted partial UI-state payload with a validated navigation value. No security follow-up is needed.

Notes

The original contributor PR was selected because it is the only linked candidate and already covered the important persistence and synchronization edge cases. It has been updated to current main and retained as the final PR so the contributor keeps authorship and discussion history.

@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Persisted UI state now includes the active top-level view, defaulting to terminal. Hydration validates stored views, handles unavailable experimental views, restores the view only during startup hydration, and preserves per-window navigation during synchronization. Debounced persistence tracks view changes, and an end-to-end test verifies that the Tasks view survives an application restart.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes implement #8264 by persisting activeView, restoring it on startup, and adding regression tests.
Out of Scope Changes check ✅ Passed The changes stay focused on activeView persistence, hydration, sync behavior, and tests; no unrelated scope stands out.
Title check ✅ Passed The title is concise and accurately summarizes the main user-visible change: persisting the active top-level view across reloads.
Description check ✅ Passed The description covers all required sections and includes testing, screenshots, AI review, security, and notes, with only minor heading variation.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/e2e/active-view-restart-restore.spec.ts (1)

101-109: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Guard each cleanup step in the finally block to prevent leaked resources.

If session.close(secondApp) throws, firstApp cleanup and session.dispose() are skipped, potentially leaving Electron processes and temp directories lingering in CI. Wrapping each close in its own try/catch ensures all cleanup steps run.

♻️ Proposed refactor
   } finally {
-    if (secondApp) {
-      await session.close(secondApp)
-    }
-    if (firstApp) {
-      await session.close(firstApp)
-    }
+    for (const app of [secondApp, firstApp]) {
+      if (app) {
+        try {
+          await session.close(app)
+        } catch (error) {
+          console.error('Failed to close Electron app during cleanup:', error)
+        }
+      }
+    }
     await session.dispose()
   }

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 964f2d1e-0f30-425b-8b09-8af5b56cb731

📥 Commits

Reviewing files that changed from the base of the PR and between 6e3ebf5 and b90b2ff.

📒 Files selected for processing (7)
  • src/renderer/src/App.tsx
  • src/renderer/src/lib/startup-ui-hydration.ts
  • src/renderer/src/store/slices/ui.test.ts
  • src/renderer/src/store/slices/ui.ts
  • src/shared/constants.ts
  • src/shared/types.ts
  • tests/e2e/active-view-restart-restore.spec.ts

Comment thread src/renderer/src/store/slices/ui.ts Outdated
@myungjuice myungjuice force-pushed the fix/persist-active-view-on-reload branch from b90b2ff to 2245593 Compare July 11, 2026 10:44

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/renderer/src/lib/startup-ui-hydration.test.ts (1)

36-48: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add activeView coverage to the fallback assertion. The fallback test only checks width/group/sort and sleeping-workspace flags, so the new activeView: 'terminal' default isn’t exercised yet.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: eb5e3f58-e74e-49b2-8660-e8d6537b91ba

📥 Commits

Reviewing files that changed from the base of the PR and between b90b2ff and 2245593.

📒 Files selected for processing (10)
  • src/renderer/src/App.tsx
  • src/renderer/src/components/automations/AutomationsPage.tsx
  • src/renderer/src/hooks/useIpcEvents.ts
  • src/renderer/src/lib/startup-ui-hydration.test.ts
  • src/renderer/src/lib/startup-ui-hydration.ts
  • src/renderer/src/store/slices/ui.test.ts
  • src/renderer/src/store/slices/ui.ts
  • src/shared/constants.ts
  • src/shared/types.ts
  • tests/e2e/active-view-restart-restore.spec.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • tests/e2e/active-view-restart-restore.spec.ts
  • src/shared/constants.ts
  • src/shared/types.ts
  • src/renderer/src/store/slices/ui.ts
  • src/renderer/src/store/slices/ui.test.ts

@myungjuice myungjuice force-pushed the fix/persist-active-view-on-reload branch from 2245593 to f8c9f38 Compare July 11, 2026 11:11
@AmethystLiang AmethystLiang self-assigned this Jul 11, 2026
@AmethystLiang AmethystLiang self-requested a review July 11, 2026 16:47
@nwparker nwparker merged commit 8b8e1bb into stablyai:main Jul 12, 2026
1 check passed
@nwparker

Copy link
Copy Markdown
Contributor

Thanks for the PR!
Will merge shortly and will be in the next or next next release

TellMeAlex added a commit to TellMeAlex/orca that referenced this pull request Jul 12, 2026
* feat(file-explorer): confirm multi-select deletes with a single batch prompt (stablyai#7459)

* feat(file-explorer): confirm multi-select deletes with a single batch prompt

Deleting a multi-file selection on a remote host prompted once per file:
requestDeleteAll awaited runDelete per root, and the remote-delete
confirmation lived inside runDelete. Hoist the confirmation for batches —
one 'Permanently delete {{count}} items?' dialog up front, then per-node
deletes with skipConfirmation. Single-file and local (Trash) deletes are
unchanged. Root filtering and the batch loop move to
file-explorer-batch-deletion.ts with unit coverage.

Fixes stablyai#7457

* fix(file-explorer): count the full selection in the batch delete prompt

Address review: roots.length undercounts when a selected directory's
children are also selected (could even read 'delete 1 items?'). Use the
visible selection size instead. Also reword the zh strings so 项目
cannot read as 'project' in a file-delete flow.

* review: skip batch delete confirm for unresolved-owner selections

Narrow the batch-confirm gate to a resolvable remote route so an
unresolved-owner multi-select no longer pops a destructive prompt for
deletes that fail closed anyway — mirroring the single-delete path.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Tom de Bres <tomdebres@users.noreply.github.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>

* fix: pr-bug-scan validated finding from stablyai#6799 (stablyai#6839)

* fix: address pr-bug-scan validated finding from stablyai#6799

Retain last-good resolved cwd on empty/error getCwd poll instead of committing null, so a transient lsof timeout no longer flips the followed worktree and resets the panel.

* Add renderer startup timing diagnostics and fix stale terminal-cwd reten

- Wraps each renderer startup hydration step (settings, repos, worktrees,
  session, SSH reconnect, etc.) with timing instrumentation gated behind
  ORCA_STARTUP_DIAGNOSTICS, wired through a new IPC channel
  (app:startupDiagnostic) so cold-start regressions can be measured.
- Fixes the Checks panel's terminal-worktree tracking to clear the
  retained cwd when the active terminal itself changes, instead of only
  retaining across transient empty/error polls on the same terminal.

---------

Co-authored-by: orca-bug-scan-bot <orca-bug-scan-bot@stably.ai>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>

* Fix stale terminal panes after backgrounding by retrying deferred foreground recovery (stablyai#8198)

* Fix stale terminal panes after backgrounding by retrying foreground reco

- Foreground recovery was skipping the replay when resume landed mid-reconnect
  (socket typically dies after 60-80s backgrounded), leaving WKWebView panes
  blank until a manual tab switch. Recovery now returns a 'deferred' outcome
  and the session screen retries it once connState flips back to connected.
- Fix a related race where a newly created tab's web-ready subscribe could be
  skipped if a lagging session-tab snapshot reset activeHandleRef before the
  subscribe fired; track the intended active handle separately.

* Fix stale pending terminal handle outliving a failed create

Clear pendingActiveTerminalHandleRef when terminal creation returns
no handle, since web-ready subscribe logic gates on this ref being
active and would otherwise see a stale value.

* Simplify GitHub issue dialog workspace section to a status label (stablyai#8327)

Remove the duplicated open/start workspace buttons and dropdown from
GHEditSection now that the primary CTA lives in the issue header;
show the attached workspace or "None yet" as plain metadata instead.

* Remove host on mobile freeze the app (stablyai#8317)

* Add host removal lifecycle safeguards and credential cleanup retry UI

- Sequence host removal so metadata commits before the client socket
  closes, avoiding a stranded host when storage fails, and add a
  cancellable open-registry to stop races between host-client opens
  and closes/unmounts.
- Queue AsyncStorage host-list mutations (rename/removal/lastConnected)
  to prevent concurrent writers from clobbering each other's changes.
- Track keychain credential cleanups that fail or time out as durable
  pending intents, surfaced with a manual retry affordance in Settings.

* Fix host removal error handling to reopen confirm dialog and alert user

Previously a failed host removal silently closed the confirm dialog,
leaving the host listed with no feedback and no easy retry path. Now
the confirm modal reopens and an alert surfaces the failure so the
user can retry.

* test: reconcile settings tests with universal right-click paste and promoted worktree symlinks

Merging main surfaced two semantic conflicts against this branch's tests:

- stablyai#8322 exposed right-click paste on every platform, so the settings
  navigation metadata now indexes it even when only the terminal host is
  Windows. Update the stale assertion accordingly.
- stablyai#8318 promoted APFS worktree shared paths by dropping the
  experimentalWorktreeSymlinks gate, so WorktreeSymlinksSection now always
  mounts inside RepositoryPane and reads window.api.fs. Stub a minimal
  renderer fs bridge in the pane test, matching the AppearancePane pattern.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>

* Fix Linux daemon shell fallback before PTY spawn (stablyai#8237)

* fix: resolve daemon shell before spawning

* fix(daemon): reconcile shell-ready barrier with the resolved fallback shell

The adapter computes shellReadySupported from the preferred shell before
spawn. When the daemon falls back (e.g. to /bin/sh), the session would
queue startup commands for the full 15s ready-marker timeout and wrap
multiline prompts in bracketed-paste markers the fallback shell cannot
parse. Expose the spawned shell on SubprocessHandle and downgrade the
barrier and paste wrapping in TerminalHost when the actual shell cannot
emit the marker.

Also apply the Unix shell fallback after the relay env scrub so a
scrubbed SHELL cannot drop the corrected value, and cover the resolve-
before-launch-config ordering, the no-shell throw, and candidate dedup
with tests.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>

* release: v1.4.137-rc.0

* fix: strip trailing whitespace from xterm webgl patch (stablyai#8330)

Blank context lines in the @xterm/addon-webgl patch had trailing spaces,
which made git diff --check fail. Strip only that whitespace and refresh
the pnpm patchedDependencies hash so the lockfile stays consistent.

* skills: prefer agent-first worktree launch; avoid empty shell tabs (stablyai#7957)

* skills: prefer agent-first worktree launch; avoid empty shell tabs

Document Orca's first-terminal behavior so agents do not leave dead
shell tabs: --agent runs in the first terminal (one tab), bare
worktree create + terminal create leaves shell + agent (two tabs).

Also: re-resolve live handles via terminal list after create, message
one handle only, and prefer orchestration check --inject over terminal
send for pure orchestration pings. Aligns with CLI docs
(--agent launches the selected agent in the first terminal).

* fix skill guidance for agent-first worktrees

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>

* fix: use dynamic agent name in orca-cli worktree fallback command (stablyai#8341)

Co-authored-by: Orca <help@stably.ai>

* Distinguish failed PR file fetches from genuinely empty PRs and add a re (stablyai#8342)

- gh file-fetch failures (rate limit, auth, unresolved remote) previously
  returned an empty array, which the Files tab rendered as "No files
  changed." — indistinguishable from a real empty PR
- getPRFiles now returns null on failure; work-item-details surfaces this
  as filesUnavailable so GitHubItemDialog and PullRequestPage can show a
  retry action instead of a misleading empty state

* release: v1.4.137-rc.1

* Fix F3: use known-tag predicate for explicit-turn decisions (stablyai#8331)

* Fix F3: use known-tag predicate for explicit-turn decisions

isHarnessInjectedUserTurnText matches any multi-word kebab tag, which is
safe for non-destructive UI classification but wrong for state-transition
decisions: a real prompt starting with a custom <my-element> paste looked
like machinery in resolvePrompt/hasExplicitUserPrompt. Combined with the
post-interrupt working-suppression in agent-hooks/server.ts, that left the
agent visibly done after Ctrl+C. Switch both explicit-turn callsites to the
known-tag isKnownHarnessInjectedUserTurnText predicate; known harness tags
and Grok <user_query> behave unchanged.

Co-authored-by: Orca <help@stably.ai>

* Retire broad harness-tag matcher; unify on known-tag predicate

The broad isHarnessInjectedUserTurnText had one remaining consumer: session
title selection in session-scanner-primary-parsers.ts. Switch it to the
known-tag predicate too — a real first turn that pastes a custom <my-element>
now titles the session instead of being demoted to the meta (fallback) title.
All observed first-turn machinery (system-reminder, caveat, command-name,
task-notification, …) is already in the known list, so the only behavior
change is that unknown, uncatalogued kebab tags stay user turns. Delete the
now-unused broad predicate and fold its coverage into the known-predicate
tests.

Co-authored-by: Orca <help@stably.ai>

* Fix bare <channel> tag being misclassified as harness machinery

Only the attributed `<channel source=…>` form is emitted by the harness;
a bare `<channel>` is legitimate user-pasted RSS/XML content. Remove
'channel' from the known-tag set (which matched any <channel ...>) and
rely solely on the existing '<channel source=' prefix rule.

---------

Co-authored-by: Orca <help@stably.ai>

* fix(browser): make fill update rich text editors via execCommand (stablyai#6060)

Rich editors reconcile browser editing transactions, while direct value/textContent writes can leave framework state stale. Classify explicit contenteditables through the requested agent-browser target, keep native fill and clear behavior for plain controls, and perform rich replacement or clear as one target-focused eval over stdin. Fail when the browser editing command is unavailable instead of presenting stale DOM state.

Preserves input/change behavior and spinbutton handling for standard fields. Verified against Draft.js and ProseMirror model state plus focused-target clear regressions.

Co-authored-by: Wolfgang Schoenberger <221313372+wolfiesch@users.noreply.github.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>

* Assign GitHub issue types (Bug/Feature) via issue templates (stablyai#8346)

Co-authored-by: Orca <help@stably.ai>

* Use worktree path to resolve HEAD OID for merged PR lookups (stablyai#8349)

- Pass the active `worktreePath` through IPC, RPC, and the GitHub client
  to ensure we fetch the correct HEAD OID when resolving merged PRs.
- Validate incoming worktree paths against known repository worktrees in
  the main process to prevent forged path usage.
- Escalate Checks Panel PR refresh requests from 'swr' to 'active' when
  a cached "no PR" miss predates when the panel became visible.

* Update .npmrc

rm exlcusions

* Fix push-target resolution missing queue-discovered PRs (stablyai#8351)

Include fallbackGitHubPR alongside linkedGitHubPR/linkedGitLabMR when
determining whether a hosted review link resolves to a push target.
Worktrees without persisted linkedPR metadata (e.g. child worktrees)
were incorrectly blocked with "target unavailable" despite having a
real matching upstream, since their PR was only known via the queue
fallback. Also splits hasPositiveHostedReviewNumberLink to build on
the resolvable subset so the two helpers can't drift.

* fix(updater): force-exit stale process so update install can relaunch (stablyai#8328)

Once quitAndInstall commits, Squirrel's ShipIt (and the Win/Linux
installers) wait for the old app process to exit before replacing the
bundle and relaunching. The quit path defers exit behind unbounded async
teardown (daemon checkpoint RPCs at 30s each per session, SSH
disconnects, watcher/emulator shutdown); if any of it wedges, the app
looks closed but the process survives, ShipIt stalls, and the update
never applies — 'Check for updates closes the app but never relaunches'.

Arm a 20s unref'd exit watchdog at the exact install-commit point (where
recovery is already forbidden) and disarm it on pre-commit recovery, so
the old process is guaranteed to exit and the installer can relaunch.

Fixes stablyai#4438

Co-authored-by: Orca <help@stably.ai>

* fix(preflight): reject Windows paths from WSL agent lookup (stablyai#7994)

* fix(preflight): reject Windows paths from WSL lookup

WSL agent discovery previously treated path.win32 absolute results as
valid guest paths, so a Windows absolute path like C:\spoof could be
counted as a found agent. Only POSIX absolute paths are valid inside WSL.

* docs(preflight): explain WSL path boundary

* docs(preflight): correct WSL path rejection rationale

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>

* fix: host browser popups in an Orca origin-bar window instead of chrome-less child windows (stablyai#8343)

* fix: host browser popups in an Orca origin-bar window instead of chrome-less child windows

A guest-opened popup previously became a default Electron child window
with no address bar, so users could not verify a popup's origin — a
phishing surface flagged in the prod-release scan of stablyai#7392. The reverted
stablyai#8332 tried gating on disposition, which is bypassable and breaks
featureless window.open() OAuth flows.

Instead, keep hosting popups in-app (preserving the shared session
partition and live window.opener handle OAuth depends on) but build the
child window ourselves via setWindowOpenHandler's createWindow callback:
a BaseWindow with an Orca-controlled origin-bar WebContentsView on top
and a content WebContentsView that adopts the pre-created popup
WebContents. The bar shows only the destination origin (never path or
query), updates on navigation, and flags plain http to remote hosts.

Also pins secure webPreferences (contextIsolation, no nodeIntegration,
sandbox, no webviewTag) on popup children via
SAFE_POPUP_WINDOW_OPTIONS, attaches guest policies to popup contents
directly (did-create-window does not fire for createWindow children),
emits the existing opened-in-orca renderer notice, and closes popups
with their opener guest.

Co-authored-by: Orca <help@stably.ai>

* fix: show page title in popup title bar instead of doubling the origin

The native title bar and the origin bar both showed the origin, reading
as a doubled header. Match Chrome popup behavior: title bar shows the
page title (reset to origin on navigation so a stale title cannot
outlive its origin); the origin bar below remains the unspoofable trust
surface.

Co-authored-by: Orca <help@stably.ai>

* fix: elide the start of long popup origins so the registrable domain stays visible

Adversarial review findings on the origin bar:

- The bar right-ellipsized long hostnames, hiding exactly the part that
  matters: window.open('https://accounts.google.com.<...>.evil.com',
  '', 'width=360') rendered as 'https://accounts.google.com.signin.s…'.
  Clip from the left instead (rtl container + isolated ltr bdi so host
  and port keep normal character order), matching how Chrome elides.
- Re-assert the origin on the popup's did-finish-load so a transiently
  dropped executeJavaScript write cannot leave a stale origin up.
- Re-pin view layout on enter/leave-full-screen: verified at runtime
  that HTML5 fullscreen keeps the bar visible on macOS (resize fires);
  the explicit events make that hold on every platform.
- Test hardening: fake WebContents now flips isDestroyed after
  'destroyed' so the double-close guard is actually exercised.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>

* fix(mobile): don't orphan pairing tokens or leak rejections on host remove (stablyai#8317) (stablyai#8354)

Prod-release-scan P1+P2 from v1.4.137-rc.1 mobile host-remove.

P1: Host remove could orphan a SecureStore pairing token with no Settings
retry when BOTH the durable pending-queue write failed AND the native delete
rejected/stalled. recordCleanupIntent swallowed the queue-write failure, so
the only recovery handle for the failed keychain delete was silently lost.
Now scheduleHostCredentialCleanup keeps a session-scoped in-memory fallback
handle when the durable write fails, so Settings still surfaces the pending
cleanup and offers a retry; confirmNativeCleanup clears the fallback if the
native delete later lands. removeHost stays non-blocking on the keychain
(freeze fix intact).

P2 (updateLastConnected): the fire-and-forget `void updateLastConnected(...)`
call site threw on unreadable storage, producing an unhandled rejection.
updateLastConnected now swallows unreadable-storage failures internally since
it's a best-effort timestamp.

P2 (soft-read): loadPendingHostCredentialCleanup now reports storageUnreadable
instead of pretending the queue is empty, and Settings surfaces a
"couldn't check cleanup status — retry to be safe" affordance rather than
hiding the section when the durable queue can't be read.

Tests: dual-fault fallback + no-clobber, storageUnreadable reporting,
fallback self-heal on late delete success, and updateLastConnected non-throw.

* Add Grok orchestration group routing (stablyai#8058)

* docs: design Grok orchestration group

* docs: plan Grok orchestration group implementation

* fix: add Grok orchestration group

* test(orchestration): accept Windows skill newlines

* Fix @grok orchestration group matching and remove stale planning docs

- Reuse the shared buildAgentNameRe matcher in groups.ts instead of a
  divergent local regex, so orchestration groups honor the same
  Windows launcher-suffix rule (grok.exe/.cmd/.bat/.ps1) as the rest
  of Orca's agent-title detection.
- Add test coverage for real Grok OSC title shapes (spinner-collapsed,
  session titles) and Windows launcher-suffix titles.
- Delete the now-completed design and implementation-plan docs for
  the Grok orchestration group work.

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>

* fix(cleanup): reconcile late-settling removals against timeout results (stablyai#8348)

* fix(cleanup): reconcile late removal results

* fix(cleanup): report authoritative late removals

* fix(cleanup): reconcile skipped ancestors on late child settlement

Ancestor rows skipped past the removal deadline were reported as
definitive failures even when the blocking child later succeeded or
was still in flight. Track provisional vs. definitive skips, re-derive
them as child settlements arrive mid-batch, requeue unblocked
ancestors for retry, and add a "still removing" toast state so batch
summaries stop contradicting rows that settle later.

* Fix skipped-parent rows going stale after post-batch child settlement

- Post-batch late child results (settling after the batch loop ends)
  previously ignored ancestor skip state, so a parent skipped for a
  since-resolved child kept a stale "could not be removed" failure
  instead of reclassifying and retrying.
- Extracts reclassification logic into
  workspace-cleanup-skipped-ancestor-reclassification.ts, shared by the
  mid-batch and new post-batch paths.
- Adds workspace-cleanup-post-batch-late-settlement.ts to reconcile
  ancestor skips and retry unblocked parents after late child
  settlement, serialized via a reconcile chain to avoid interleaving
  concurrent settlements.

* fix(pi): keep status hooks off the Pi turn critical path (stablyai#8355)

Pi awaits its extension event handlers, so an awaited loopback status
post that stalls (Orca restarting / receiver unavailable) blocked the
running Pi turn and disconnected it. Make post() fire-and-forget with a
latest-only pending slot drained by a single active request and a 1s
AbortController timeout, so a stalled receiver can never hold the turn.

Also stop treating session_shutdown as turn completion: Pi emits it on
reload/new/resume/fork while the PTY stays alive, so only agent_end
proves done (real exit is cleared by PTY teardown). Split the generated
handler registrations into agent-status-handler-source.ts.

Co-authored-by: Orca <help@stably.ai>

* Cli destructive suggest (stablyai#8352)

* fix(cli): don't recover benign typos into destructive commands

CLI did-you-mean ranked purely by Levenshtein, so `orca worktree move`
sole-suggested `orca worktree remove` (distance 2) — an alias of the
destructive `worktree rm`. Suggestions also flow into --json
error.data.nextSteps, the agent recovery channel, so a blind retry could
delete a clean worktree.

Make destructiveness a declared property of the command instead of a verb
heuristic: add `destructive?: true` to CommandSpec and mark the
irreversible commands (worktree rm, environment rm, automations remove,
project setup-delete, tab profile delete, cookie delete, storage
local/session clear). The suggestion ranker excludes destructive
candidates unless the input token is itself a near-miss (distance <=1) of a
destructive verb, so `worktree remov` still recovers `rm`/`remove` while
`worktree move` no longer does. The guard tracks the registry, so it
also covers destructive verbs outside the delete family (e.g. kill).

Fixes stablyai#6303

Co-authored-by: Orca <help@stably.ai>

* fix(cli): use Array.at(-1) to satisfy oxlint prefer-at

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>

* Naming usage ui copy (stablyai#8357)

* fix: work-item naming, usage % rounding, and terminal/delete copy

Address prod-release-scan P2s:

- stablyai#8238: recognize Bitbucket Server (/projects|users/.../repos/.../pull-requests/N)
  and Azure DevOps (/_git/REPO/pullrequest/N) PR URL shapes in
  work-item-reference, alongside Bitbucket Cloud; graceful fallback preserved.

- stablyai#7574: getDisplayedUsagePercentage now rounds the used value before taking the
  `remaining` complement, so the compact status bar (raw usedPercent) and tooltip
  (pre-rounded clampUsedPercent) can no longer disagree by 1% at a .5 fraction.
  clampUsedPercent moves to the shared module as the single rounding source.

- stablyai#7459: mixed remote+local batch delete confirm no longer claims the whole
  batch is a permanent "remote host" delete — it now states remote items are
  permanent while local items move to the Trash/Recycle Bin.

- stablyai#8322: right-click-to-paste settings copy is platform-aware — "Control-click"
  on macOS, "Ctrl+right-click" on Windows/Linux — matching the ctrlKey gate.

Localization catalog synced (also picks up pre-existing UsagePercentageDisplayChangeNotice drift).

* Fix NaN% usage bar and label for non-finite provider values

Non-finite usedPercent inputs (NaN/Infinity) propagated through Math.round/min/max into the CSS bar width (`NaN%`) and displayed copy. clampUsedPercent now short-circuits to 0 in that case, with a test covering the divergence from getDisplayedUsagePercentage for the 'remaining' case.

* fix(runtime): explain full worktree id selectors (stablyai#7432) (stablyai#7892)

* fix(runtime): explain full worktree id selectors (stablyai#7432)

* Fix full worktree id selectors for bare repo ids and doc guidance

- Reject bare repo-id selectors up front via a shared validator instead
  of relying on worktree-list scanning, so RPC callers no longer trigger
  an unnecessary rescan just to detect the mistake
- Propagate the structured worktree_id_requires_full_path code through
  RPC error mapping so callers get a typed error, not just a message
- Update orca-cli, orca-emulator, and orchestration skill docs to show
  the full `<repo-id>::<path>` id shape and stop implying a bare repo
  id is a valid worktree selector

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>

* docs(computer-use): clarify get-app-state JSON tree field (stablyai#7788)

* docs(computer-use): document get-app-state JSON response fields

* docs(computer-use): correct get-app-state JSON guidance

---------

Co-authored-by: 循安 林 <andylin2@fmt.com.tw>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>

* Update README downloads badge

* Fix Codex WSL project trust conflating case-distinct Linux paths (stablyai#8333)

* Fix Codex WSL project trust conflating case-distinct Linux paths

normalizeCodexProjectPathForLookup lowercased the entire Windows/UNC
path, including the case-sensitive Linux portion under \\wsl$\<distro>.
Two distinct WSL project dirs (.../Repo vs .../repo) collapsed onto one
trust key, and the mirror dedupe (codex-config-mirror) inherited it.

Preserve case for the Linux path after the case-insensitive
\\wsl$\<distro> / \\wsl.localhost\<distro> share prefix; true Windows
drive letters and normal UNC shares still case-fold as before.

Co-authored-by: Orca <help@stably.ai>

* Apply the same WSL case-fold fix to hook-trust key lookup

normalizeHookTrustKeyForLookup had the identical latent bug: on a win32
host it lowercased the whole Windows-shaped path, folding the
case-sensitive Linux tail of a \\wsl$\<distro> UNC hook path — contrary
to its own comment that WSL sources stay case-sensitive.

Extract foldWindowsCaseInsensitivePath (shared with the project-path
normalizer): fold only the case-insensitive drive/\\wsl$ share prefix,
preserve the Linux tail. Existing suffix (event:group:handler) is already
lowercase, so behavior is unchanged there.

Co-authored-by: Orca <help@stably.ai>

* Fix Codex WSL trust revocation and hook-key case folding

- Case-drifted or share-spelling-varied WSL revocations were being
  ignored on merge, letting stale "trusted" entries survive; fold
  revocation lookups fully so matching errs toward revoked.
- Hook-trust key folding was gated on process.platform === 'win32',
  so WSL/SSH-remote hook sources weren't folded when Orca itself ran
  on macOS/Linux; fold by path shape instead, via the new shared
  foldWslUncPathCaseInsensitiveParts helper (also covers /mnt drvfs
  automounts and wsl$/wsl.localhost share aliasing).

* Fix Codex WSL trust key case-folding regressions

- Don't fold case-variant `/MNT` dirs as if they were the drvfs
  automount; only literal lowercase `/mnt/<drive>` folds.
- Preserve an exact-cased trusted project entry in ~/.codex during
  runtime merge instead of letting a loosely-matched, case-drifted
  revocation clobber a user's re-granted trust on every mirror pass.
- Minor cleanup: inline foldWindowsCaseInsensitivePath and hoist the
  repeated normalizeHookTrustKeyForLookup call in findTrustBlockRanges.

* Refactor case-fold WSL trust test to assert real serializer output

Extract path variables and assert against escapeTomlString(incomingPath) instead of a hardcoded escaped string literal, so the fixture can't silently drift from the actual TOML header serialization.

---------

Co-authored-by: Orca <help@stably.ai>

* fix(linux): stop Orca terminals from launching the GNOME Orca screen reader via bare `orca` (stablyai#8347)

On Linux the CLI installs as orca-ide so it never shadows /usr/bin/orca
(GNOME's screen reader), but agent-facing surfaces (orca-cli skill,
dispatch preambles, CLI hints) all invoke bare `orca` — so on stock
Ubuntu an agent inside an Orca terminal launched the screen reader,
which started speaking (stablyai#7904).

Fix: prepend a userData-scoped shim dir (bare `orca` -> bundled
orca-ide launcher, or the stable AppImage) to the PATH of every
packaged-Linux managed PTY, mirroring the existing dev-mode cli/bin
prepend. The user's own shells — and their real screen reader command —
stay untouched. Also flip the orca-cli skill probe to prefer orca-ide
so agents outside Orca terminals never execute the screen reader.

Fixes stablyai#7904

Co-authored-by: Orca <help@stably.ai>

* release: v1.4.138-rc.0

* feat(ssh): support file transfers over system ssh (stablyai#7804)

* feat(ssh): support file transfers over system ssh

* fix(ssh): harden system file transfers

* test(runtime): stub getRepo in headless mobile tab cwd test

The mobile-session selector validator (getValidatedExplicitWorktreeIdSelector,
from main) calls this.store?.getRepo to reject repo ids passed as worktree ids.
The store stub only implemented getWorkspaceSession, so the guarded call threw
'getRepo is not a function' once main merged into this branch. Add a getRepo
that returns null (wt-1 is a worktree, not a repo).

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>

* fix(codex): share the user's global AGENTS.md into the managed Codex runtime home (stablyai#7927)

* fix(codex): share global AGENTS.md into managed runtime homes

Orca launches Codex with a managed CODEX_HOME, so host and WSL sessions otherwise lose the user's global instructions. Share AGENTS.md through the existing ownership-aware link-or-copy path, with a narrow WSL launch sync that avoids copying unrelated resource directories.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(codex): copy global AGENTS.md into WSL runtime homes

Symlinking across the \wsl.localhost 9P boundary stores a Windows UNC
target the distro cannot resolve, so the global AGENTS.md silently would
not load inside WSL when the host symlink succeeded. Copy it like the
config mirror already does across the same boundary.

Co-authored-by: Orca <help@stably.ai>

* Fix WSL global-instruction sync to dereference symlinks and skip redunda

- Copy AGENTS.md contents (not the symlink) into WSL runtime homes, since
  a host-side symlink is unusable inside the distro
- Skip rewriting the fallback copy when source contents are unchanged,
  avoiding needless writes across the UNC boundary on every Codex launch
- Handle non-regular-file sources and malformed/undeletable marker
  directories without blocking sync or stranding stale instructions

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>

* Resolve and require explicit push targets for linked reviews (stablyai#6159)

Automatically resolve and require explicit push targets for linked
GitHub PRs and GitLab MRs before allowing remote actions.

This prevents operations like push or sync from targeting helper
upstreams or falling back to default behaviors when the review target
is unavailable. Also, recover missing push targets during metadata
saves to heal existing linked PRs.

* release: v1.4.138-rc.1

* fix(settings): make WSL skill commands pasteable (stablyai#7795) (stablyai#7881)

* fix(settings): make WSL skill commands pasteable (stablyai#7795)

* Fix WSL skill commands so PowerShell 7 pastes match PowerShell 5.1 argv

- Encode the WSL login-shell script as base64 and decode/eval it inside
  the sh -c invocation, avoiding raw nested quotes at the paste boundary
- Scope $PSNativeCommandArgumentPassing = 'Legacy' to the invocation so
  PS 5.1 and PS 7 both hand wsl.exe the same escaped argv
- Extract powershell-native-argument.ts as the shared quoting module and
  reuse it from ssh-remote-powershell.ts

* test(runtime): stub getRepo in mobile-tab startup cwd test

Main's stablyai#7892 made listMobileSessionTabs validate selectors via
this.store?.getRepo; the mock store here only defined
getWorkspaceSession, so the merged CI build threw 'getRepo is not a
function'. Return null (wt-1 is a worktree id, not a repo).

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>

* release: v1.4.138-rc.2

* Remove redundant line on PR evidence images

Removed redundant line about committing PR evidence images.

* fix: restore the active top-level view across renderer reload (stablyai#8265)

Persist and safely restore the active top-level view on startup. Unknown, removed, legacy, or unavailable views fall back to the terminal, while cross-window UI sync cannot navigate the current window.\n\nCloses stablyai#8264

* fix(cli): preserve multiline arguments on Windows (stablyai#8374)

* fix(cli): preserve multiline Windows arguments

* test(cli): run Windows launcher regression in CI

* fix(cli): support Windows Framework C# compiler

* fix(orchestration): complete tasks on worker_done + coordinator UX fixes (stablyai#8030)

* fix(orchestration): complete worker tasks and improve coordinator UX

* Fix orchestration lifecycle sender resolution and peek/check compat hand

- Lifecycle sends (worker_done/heartbeat) now use ORCA_TERMINAL_HANDLE
  verbatim, skipping the liveness probe and pane remint that could
  block delivery during restarts or mismatch stale-runtime assignee
  handles.
- --peek now round-trips as {peek:true, unread:false} so older runtimes
  that strip unknown params degrade to non-destructive "all" instead of
  mark-read, with client-side filtering to restore peek semantics and a
  clear error when --peek --wait can't be honored.
- Reject combined read-mode flags (--unread/--peek/--all) before calling
  the runtime.
- Distinguish suppressed (already-consumed) lifecycle messages from
  ignored ones so send doesn't wake --wait waiters for stale heartbeats.
- Fix task summary truncation to avoid splitting UTF-16 surrogate pairs
  and to not misreport whitespace normalization as truncation.

* Add shared helper to abbreviate orchestration task specs for brief listi

- Normalizes whitespace and caps spec length at 160 chars, flagging
  truncation separately from whitespace-only changes
- Truncates on UTF-16 code point boundaries to avoid splitting
  surrogate pairs and emitting malformed strings

* Add pane-key identity to worker_done/heartbeat reconciliation and server

- Records the sender's pane key on messages and dispatch contexts so
  worker_done/heartbeat ownership can be verified by the remint-stable
  pane leaf instead of the terminal handle, which is reissued across
  restarts.
- Rejects lifecycle messages from a genuinely foreign pane while still
  tolerating handle remints, tab break-outs, and older CLIs that lack
  pane identity.
- Moves task-spec abbreviation server-side (orchestration.taskList
  --brief) so full specs no longer cross SSH/relay transports, with a
  client-side fallback for older runtimes; consolidates the shared
  abbreviation helper under src/shared.
- Adds a stderr warning when a pre-peek runtime's --peek response hits
  the 100-row cap, since older unread messages may be missing.

* Isolate ORCA_PANE_KEY in CLI test beforeEach to fix leaked senderPaneKey

Co-authored-by: Orca <help@stably.ai>

* Fix pane-key remint bypassing dispatch mutual-exclusion lock

- Dispatch locking only matched on assignee_handle, so a reminted
  terminal handle (tab break-out) could open a second concurrent
  dispatch on the same pane.
- Add leaf-UUID-based pane key comparison (parsePaneKey) as a
  secondary lock, falling back to exact handle match for legacy
  rows without pane keys.

* Update orchestration skill docs for lifecycle authority and CLI flag add

- Clarify that dispatch lifecycle is tied to taskId+dispatchId verified against
  the dispatched pane, not the terminal handle, since handles can be reminted
  after restart
- Document new `check --peek`/`--all` and `task-list --brief` flags, with
  fallback guidance for older CLIs that reject them
- Note that a valid worker_done auto-completes the task/dispatch, so workers
  shouldn't also call task-update manually

---------

Co-authored-by: Orca <help@stably.ai>

* ci(fork): skip upstream community tracker

---------

Co-authored-by: Tom <debres.bot@gmail.com>
Co-authored-by: Tom de Bres <tomdebres@users.noreply.github.com>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: buf0-bot[bot] <252831055+buf0-bot[bot]@users.noreply.github.com>
Co-authored-by: orca-bug-scan-bot <orca-bug-scan-bot@stably.ai>
Co-authored-by: Vitus <zhzvitus@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: ryushione <108252882+ryushione@users.noreply.github.com>
Co-authored-by: Wolfie <wolfgangs2000@gmail.com>
Co-authored-by: Wolfgang Schoenberger <221313372+wolfiesch@users.noreply.github.com>
Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: BingZ <techzbing@gmail.com>
Co-authored-by: Rod Boev <rod.boev@gmail.com>
Co-authored-by: z116123123x <52649964+z116123123x@users.noreply.github.com>
Co-authored-by: 循安 林 <andylin2@fmt.com.tw>
Co-authored-by: gatsby74 <166927047+gatsby74@users.noreply.github.com>
Co-authored-by: Robot. <topgrd@outlook.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Myungjoo Jang <wkdaudwn11@naver.com>
@myungjuice myungjuice deleted the fix/persist-active-view-on-reload branch July 12, 2026 12:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Active top-level view is not restored after a renderer reload (always resets to the terminal)

3 participants